programming4us
           
 
 
Programming

iPad SDK : Preparing Dudel for a New Tool (part 3) - Creating the Text Tool

- Free product key for windows 10
- Free Product Key for Microsoft office 365
- Malwarebytes Premium 3.7.1 Serial Keys (LifeTime) 2019
1/15/2011 4:18:14 PM

4. Creating the Text Tool

With that in place, we can now create the TextTool class itself. This class is a lot like the RectangleTool class, but with an extra twist: When the user finishes drawing a rectangle, this tool switches into text-editing mode by placing a UITextView at the location where the rectangle was drawn. It's set up as the first responder so that the keyboard will appear, and users can enter their text. It also shows a gray shade over the rest of the screen, to give the users some focus so they can see where they're typing, as shown in Figure 2.

Figure 2. The text field's so bright; you gotta show shade.


When the user presses the bottom-right keyboard button to dismiss the keyboard, or taps anywhere else in the drawing area, the text entry is considered complete. At that point, the tool creates an instance of a new class called TextDrawingInfo (which we haven't created yet). That will be another class that implements the Drawable protocol , and can therefore be added to the list of things that DudelView needs to draw.

4.1. Declaring the Text Tool Interface

Create a new class called TextTool, and start defining it by putting this code into TextTool.h:

//  TextTool.h
#import <Foundation/Foundation.h>
#import "Tool.h"

@interface TextTool : NSObject <Tool, UITextViewDelegate> {
id <ToolDelegate> delegate;
NSMutableArray *trackingTouches;
NSMutableArray *startPoints;
UIBezierPath *completedPath;
CGFloat viewSlideDistance;
}
@property (retain, nonatomic) UIBezierPath *completedPath;
+ (TextTool*)sharedTextTool;
@end

Most of what's declared here is pretty similar to what our other tools had. The one new addition is the viewSlideDistance, which we'll use to determine how far to shift the view in case it's being covered up by the on-screen keyboard (more on that in a page or two).

4.2. Implementing TextTool

The file containing the implementation, TextTool.m, so I'll interject some additional information at the tricky spots as we go through it. Start off with some #imports:

//  TextTool.m

#import "TextTool.h"
#import "TextDrawingInfo.h"

#import "SynthesizeSingleton.h"

After the rectangle is drawn, we'll create a temporary view, which will need to be cleaned up later. Instead of using an instance variable for this, we're going to assign a tag number for later retrieval.

#define SHADE_TAG 10000

Later on, we'll check the distance between the drawn rectangle's start and end points to see if we think it's big enough to contain any text. This function will help us out.

static CGFloat distanceBetween(const CGPoint p1, const CGPoint p2) {
// Pythagoras in the house!
return sqrt(pow(p1.x-p2.x, 2) + pow(p1.y-p2.y, 2));
}

@implementation TextTool
@synthesize delegate, completedPath;
SYNTHESIZE_SINGLETON_FOR_CLASS(TextTool);
- init {
if ((self = [super init])) {
trackingTouches = [[NSMutableArray array] retain];
startPoints = [[NSMutableArray array] retain];
}
return self;
}
- (void)activate {
}
- (void)deactivate {
[trackingTouches removeAllObjects];
[startPoints removeAllObjects];
self.completedPath = nil;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UIView *touchedView = [delegate viewForUseWithTool:self];
[touchedView endEditing:YES];

This tool should allow the user to drag out only one rectangle at a time. If we let it do more, how would we know which one should get the text? So here, instead of dealing with all the touches, we just ask for any one of them.
UITouch *touch = [[event allTouches] anyObject];

Remember the touch, and its original start point, for future reference.

[trackingTouches addObject:touch];
CGPoint location = [touch locationInView:touchedView];
[startPoints addObject:[NSValue valueWithCGPoint:location]];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UIView *touchedView = [delegate viewForUseWithTool:self];
for (UITouch *touch in [event allTouches]) {
NSUInteger touchIndex = [trackingTouches indexOfObject:touch];

We continue with the rest only if we actually remember the start of this touch. We might be seeing a simultaneous touch that we ignored earlier.

if (touchIndex != NSNotFound) {
CGPoint startPoint = [[startPoints objectAtIndex:touchIndex] CGPointValue];
CGPoint endPoint = [touch locationInView:touchedView];
[trackingTouches removeObjectAtIndex:touchIndex];
[startPoints removeObjectAtIndex:touchIndex];


Detect short taps that are too small to contain any text. These are probably accidents.

if (distanceBetween(startPoint, endPoint) < 5.0) return;

Make a rectangle that stretches from the start point to the current point, and wrap that in a path.

CGRect rect = CGRectMake(startPoint.x, startPoint.y,
endPoint.x - startPoint.x, endPoint.y - startPoint.y);
self.completedPath = [UIBezierPath bezierPathWithRect:rect];


Draw a shaded area over the entire view, so that the users can easily see where to focus their attention.

UIView *backgroundShade = [[[UIView alloc] initWithFrame:touchedView.bounds]
autorelease];
backgroundShade.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.5];
backgroundShade.tag = SHADE_TAG;
backgroundShade.userInteractionEnabled = NO;
[touchedView addSubview:backgroundShade];

Now comes the fun part. We make a temporary UITextView for the actual text input, and set ourselves up to receive notifications when that input begins and ends.
UITextView *textView = [[[UITextView alloc] initWithFrame:rect] autorelease];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];


Anyone dealing with text input on the iPhone has probably had to tackle the problem of displaying content that may be obscured by the on-screen keyboard. Here in Dudel, we're going to have the same problem, since users can easily drag a text rectangle in the lower half of the screen.

The following code determines how far the main view needs to be shifted to account for the current rectangle, based on the current orientation and the size of the on-screen keyboard. This value is stored in the viewSlideDistance variable. It will be used later when the keyboard slides into place, and again when it slides back out. Although a user can still create a text rectangle so tall that it will be partly obscured, by doing the following, we're at least making a solid effort and covering the most common cases.

CGFloat keyboardHeight = 0;
UIInterfaceOrientation orientation =
((UIViewController*)delegate).interfaceOrientation;
if (UIInterfaceOrientationIsPortrait(orientation)) {
keyboardHeight = 264;
} else {
keyboardHeight = 352;
}
CGRect viewBounds = touchedView.bounds;
CGFloat rectMaxY = rect.origin.y + rect.size.height;
CGFloat availableHeight = viewBounds.size.height - keyboardHeight;
if (rectMaxY > availableHeight) {
// calculate a slide distance so that the dragged box is centered vertically
viewSlideDistance = rectMaxY - availableHeight;
} else {


viewSlideDistance = 0;
}

textView.delegate = self;
[touchedView addSubview:textView];

This next part is a bit of a trick. Due to a bug in UITextView, just telling it to become the first responder doesn't actually make it happen. The users typically must tap it once on their own to make the keyboard pop up. Toggling the editable flag is a work-around that makes the keyboard actually appear.

textView.editable = NO;
textView.editable = YES;
[touchedView becomeFirstResponder];
}
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (void)drawTemporary {
if (self.completedPath) {
[delegate.strokeColor setStroke];
[self.completedPath stroke];
} else {
UIView *touchedView = [delegate viewForUseWithTool:self];
for (int i = 0; i<[trackingTouches count]; i++) {
UITouch *touch = [trackingTouches objectAtIndex:i];
CGPoint startPoint = [[startPoints objectAtIndex:i] CGPointValue];
CGPoint endPoint = [touch locationInView:touchedView];
CGRect rect = CGRectMake(startPoint.x, startPoint.y, endPoint.x - startPoint.x,
endPoint.y - startPoint.y);
UIBezierPath *path = [UIBezierPath bezierPathWithRect:rect];
[delegate.strokeColor setStroke];
[path stroke];
}
}
}
- (void)dealloc {
self.completedPath = nil;
[trackingTouches release];
[startPoints release];
self.delegate = nil;
[super dealloc];
}

These are the methods that are triggered by the hiding and showing of the keyboard. When the keyboard slides into place, it covers up the lower portion of the display. Here, we handle this by shifting things a bit if the rectangle we're operating on is covered up.
- (void)keyboardWillShow:(NSNotification *)aNotification {
UIInterfaceOrientation orientation =
((UIViewController*)delegate).interfaceOrientation;
[UIView beginAnimations:@"viewSlideUp" context:NULL];
UIView *view = [delegate viewForUseWithTool:self];
CGRect frame = [view frame];
switch (orientation) {


case UIInterfaceOrientationLandscapeLeft:
frame.origin.x -= viewSlideDistance;
break;
case UIInterfaceOrientationLandscapeRight:
frame.origin.x += viewSlideDistance;
break;
case UIInterfaceOrientationPortrait:
frame.origin.y -= viewSlideDistance;
break;
case UIInterfaceOrientationPortraitUpsideDown:
frame.origin.y += viewSlideDistance;
break;
default:
break;
}
[view setFrame:frame];
[UIView commitAnimations];
}
- (void)keyboardWillHide:(NSNotification *)aNotification {
UIInterfaceOrientation orientation =
((UIViewController*)delegate).interfaceOrientation;
[UIView beginAnimations:@"viewSlideDown" context:NULL];
UIView *view = [delegate viewForUseWithTool:self];
CGRect frame = [view frame];
switch (orientation) {
case UIInterfaceOrientationLandscapeLeft:
frame.origin.x += viewSlideDistance;
break;
case UIInterfaceOrientationLandscapeRight:
frame.origin.x -= viewSlideDistance;
break;
case UIInterfaceOrientationPortrait:
frame.origin.y += viewSlideDistance;
break;
case UIInterfaceOrientationPortraitUpsideDown:
frame.origin.y -= viewSlideDistance;
break;
default:
break;
}
[view setFrame:frame];
[UIView commitAnimations];
}


This method, declared in the UITextViewDelegate protocol, is called when the user taps outside the textView or dismisses the keyboard. Here, we create the TextDrawingInfo object (which we'll define shortly) that contains the entered text, along with the current font and color choices. Then we get rid of the temporary views we created earlier.

- (void)textViewDidEndEditing:(UITextView *)textView {
NSLog(@"textViewDidEndEditing");
TextDrawingInfo *info = [TextDrawingInfo textDrawingInfoWithPath:completedPath
text:textView.text
strokeColor:delegate.strokeColor
font:delegate.font];
[delegate addDrawable:info];
self.completedPath = nil;


UIView *superView = [textView superview];
[[superView viewWithTag:SHADE_TAG] removeFromSuperview];
[textView resignFirstResponder];
[textView removeFromSuperview];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end

So that's the Text tool. It's fairly complex, but that's actually a good thing! All the intricacies of entering text are in one place, and the rest of our architecture requires minimal changes in order to deal with it. You've already seen the few changes that DudelViewController needed, and the DudelView class itself requires no changes at all! All that's left now is to define the TextDrawingInfo class.

Other -----------------
- Coding JavaScript for Mobile Browsers (part 9) - Scripting Styles
- Coding JavaScript for Mobile Browsers (part 8) - DOM
- Coding JavaScript for Mobile Browsers (part 7)
- Coding JavaScript for Mobile Browsers (part 6)
- iPad SDK : The Structure of Core Text
- iPad SDK : PDF Generation
- jQuery 1.3 : Sorting and paging (part 5) - Finessing the sort keys
- jQuery 1.3 : Sorting and paging (part 4)
- jQuery 1.3 : Sorting and paging (part 3) - Using a comparator to sort table rows
- jQuery 1.3 : Sorting and paging (part 2) - JavaScript sorting
- jQuery 1.3 : Sorting and paging (part 1) - Server-side sorting
- Coding JavaScript for Mobile Browsers (part 5)
- Coding JavaScript for Mobile Browsers (part 4)
- Coding JavaScript for Mobile Browsers (part 3) - Writing to the document
- Coding JavaScript for Mobile Browsers (part 1) - Standard dialogs
- Coding JavaScript for Mobile Browsers (part 1) - Code Execution
- Programming the Mobile Web : JavaScript Mobile - Supported Technologies
- Security in Cloud Computing (part 4) - Audit and Compliance
- Security in Cloud Computing (part 3)
- Security in Cloud Computing (part 2) - Identity and Access Management
 
 
 
Top 10
 
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
programming4us programming4us